/*
*  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
*  reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*  1. Redistributions of source code must retain the above copyright
*  notice, this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright
*  notice, this list of conditions and the following disclaimer in
*  the documentation and/or other materials provided with the
*  distribution.
*
*  3. The end-user documentation included with the redistribution,
*  if any, must include the following acknowledgment:
*  "This product includes software developed by the
*  Sun Microsystems, Inc. for Project JXTA."
*  Alternately, this acknowledgment may appear in the software itself,
*  if and wherever such third-party acknowledgments normally appear.
*
*  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
*  must not be used to endorse or promote products derived from this
*  software without prior written permission. For written
*  permission, please contact Project JXTA at http://www.jxta.org.
*
*  5. Products derived from this software may not be called "JXTA",
*  nor may "JXTA" appear in their name, without prior written
*  permission of Sun.
*
*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
*  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*  ====================================================================
*
*  This software consists of voluntary contributions made by many
*  individuals on behalf of Project JXTA.  For more
*  information on Project JXTA, please see
*  <http://www.jxta.org/>.
*
*  This license is based on the BSD license adopted by the Apache Foundation.
*
*  $Id: PeerChatAction.java,v 1.5 2007/05/22 06:15:29 nano Exp $
*/

package net.jxta.myjxta.plugins.secureonetoone;

import net.jxta.document.*;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.TextDocumentMessageElement;
import net.jxta.logging.Logging;
import net.jxta.myjxta.View;
import net.jxta.myjxta.dialog.Dialog;
import net.jxta.myjxta.dialog.DialogManager;
import net.jxta.myjxta.dialog.DialogMessage;
import net.jxta.myjxta.dialog.OneToOneDialog;
import net.jxta.myjxta.dialog.util.RemoteCommandInvoker;
import net.jxta.myjxta.util.CredentialUtil;
import net.jxta.myjxta.util.Group;
import net.jxta.myjxta.util.Peer;
import net.jxta.myjxta.util.Resources;
import net.jxta.myjxta.util.objectmodel.GroupNode;
import net.jxta.myjxta.util.objectmodel.PeerNode;
import net.jxta.protocol.PipeAdvertisement;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author james todd [gonzo at jxta dot org]
 * @version $Id: PeerChatAction.java,v 1.5 2007/05/22 06:15:29 nano Exp $
 */

public class PeerChatAction
        extends AbstractAction {

    private static final int INTERVAL = 250;
    private static final int MAX = INTERVAL * 4 * 75;
    private static final ResourceBundle STRINGS = Resources.getStrings();
    private static final Logger LOG = Logger.getLogger(PeerChatAction.class.getName());

    private View view = null;

    public PeerChatAction(String name, View view) {
        super(name);

        this.view = view;

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("PeerChatAction instantiated");
        }
    }

    protected PeerNode getPeerForAction() {
        return (PeerNode) view.getJxtaNode(PeerNode.class);
    }

    protected Group getGroupForAction() {
        PeerNode pn = (PeerNode) view.getJxtaNode(PeerNode.class);
        return pn != null ? ((GroupNode) (pn.getParent())).getGroup() : null;
    }

    public final void actionPerformed(ActionEvent ae) {
        final PeerNode pn = getPeerForAction();
        final Peer p = pn.getPeer();
        final Group g = getGroupForAction();
        String status;
        if (p == null) {
            return; //should never happen!
        }
        if (g != null) {
            status = STRINGS.getString("status.command.initiate") +
                    ": " + p.getName();

            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info(status);
            }

            new Thread(new Runnable() {
                public void run() {
                    pn.setInfo(PeerNode.CONNECTION_ONGOING, Boolean.TRUE.toString(), true);
                    initSecureConnection(g, p);
                    pn.setInfo(PeerNode.CONNECTION_ONGOING, null, true);
                }
            }, getClass().getName() + ":getConnection").start();
        } else {
            status = STRINGS.getString("error.peer.invalid") +
                    " " + p.getName();

            if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                LOG.severe(status);
            }
        }

        if (status != null) {
            this.view.setStatus(status);
        }
    }

    public boolean isEnabled() {
        PeerNode jxtaNode = (PeerNode) view.getJxtaNode(PeerNode.class);
        return jxtaNode != null; // we need a check that this isnt our own peer
    }

    private void initSecureConnection(Group g, Peer p) {
        String status = STRINGS.getString("status.peer.1to1.certificate.request") +
                " " + p.getName();

        this.view.setStatus(status);

        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info(status);
        }

        StructuredDocument ownCredentials = CredentialUtil.getCredential(this.view, g);
        TextDocumentMessageElement ownCredElement = ownCredentials != null ?
                new TextDocumentMessageElement(CredentialCommand.CREDENTIAL,
                        (StructuredTextDocument) ownCredentials, null) : null;

        if (ownCredElement == null) {
            if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                LOG.severe("can\'t construct document");
            }
            view.showMessageDialog(STRINGS.getString("error.peer.1to1.authentication.needed"));
            return;
        }

        status = STRINGS.getString("status.peer.1to1.certificate.request") +
                ": " + p.getName();

        this.view.setStatus(status);

        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info(status);
            LOG.info("instantiating CredentialCommand");
        }

        RemoteCommandInvoker commandInvoker = new RemoteCommandInvoker(g, p.getPipeAdvertisement(),
                new CredentialCommand(), this.view.getControl());

        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info("dispatch command");
        }

        commandInvoker.invoke(MAX);

        DialogMessage r = commandInvoker.getResponse();


        if (commandInvoker.getPipeExeption() != null) {
            view.showMessageDialog(p.getName() + ":" + STRINGS.getString("error.peer.1to1.connection.failed") + commandInvoker.getPipeExeption().getMessage());
            return;
        }

        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info("CredentialCommand response: " + r);
        }

        MessageElement ce = r != null ? r.getMessageElement(CredentialCommand.CREDENTIAL) : null;

        if (ce == null) {
            status = STRINGS.getString("error.peer.1to1.certificate.incomplete") + ": " + p.getName();
            this.view.setStatus(status);
            this.view.showMessageDialog(status);

            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("bad response: " + status);
            }

            view.showMessageDialog(p.getName() + ":" + STRINGS.getString("error.peer.1to1.connection.failed") + commandInvoker.getPipeExeption().getMessage());
            return;

        }

        status = STRINGS.getString("status.peer.1to1.certificate.import") +
                ": " + p.getName();

        this.view.setStatus(status);

        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info(status);
        }


        boolean isValid = CredentialUtil.importCredential(ce, g, this.view.getControl());


        if (isValid) {
            status = STRINGS.getString("status.peer.1to1.pipe.request") + ": " + p.getName();

            this.view.setStatus(status);

            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info(status);
                LOG.info("instantiating UnicastSecurePipeAdvCommand");
            }

            commandInvoker = new RemoteCommandInvoker(g, p.getPipeAdvertisement(), new UnicastSecurePipeAdvCommand(), this.view.getControl());

            Map<String, MessageElement> m = new HashMap<String, MessageElement>();

            m.put(CredentialCommand.CREDENTIAL, ownCredElement);

            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("dispatch command");
            }

            commandInvoker.invoke(MAX, m);

            r = commandInvoker.getResponse();

            if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
                LOG.info("UnicastSecurePipeAdvCommand response: " + r);
            }

            MessageElement pae = r != null ? r.getMessageElement(UnicastSecurePipeAdvCommand.PIPE) : null;

            if (pae != null) {
                status = STRINGS.getString("status.peer.1to1.pipe.import") + ": " + p.getName();
                this.view.setStatus(status);

                if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                    LOG.fine(status);
                    LOG.fine("adding dialog");
                }

                Dialog dialog = DialogManager.getDialog(OneToOneDialog.class,
                        g, getPipeAdv(pae), this.view.getControl());
                if (dialog instanceof OneToOneDialog) {
                    OneToOneDialog oneToOneDialog = (OneToOneDialog) dialog;
                    oneToOneDialog.setInitialName(p.getName());
                }
                this.view.getControl().addDialog(
                        dialog);
            } else {
                status = STRINGS.getString("error.peer.1to1.pipe.secureConnectionIncomplete") +
                        ": " + p.getName();
                this.view.showMessageDialog(status);
                this.view.setStatus(status);

                if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                    LOG.severe(status);
                }
            }
        } else {
            view.showMessageDialog(STRINGS.getString("error.certificate.generalerror"));
            if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                LOG.severe("invalid response");
            }
        }
    }

    private PipeAdvertisement getPipeAdv(MessageElement me) {
        if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
            LOG.info("get pipeAdv");
        }

        XMLElement sd = null;

        try {
            sd = (XMLElement) StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, me.getStream());
        } catch (IOException ioe) {
            if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                LOG.log(Level.SEVERE, "can\'t document", ioe);
            }
        }

        PipeAdvertisement pa = null;

        if (sd != null) {
            pa = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(sd);
        }

        return pa;
    }
}
